package org.hentor.modules.generate.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.hentor.common.util.RedisUtil;
import org.hentor.common.vo.Result;
import org.hentor.modules.generate.dto.GenerateCodeDTO;
import org.hentor.modules.generate.dto.GenerateDevSaveDTO;
import org.hentor.modules.generate.dto.GenerateDevSelectDTO;
import org.hentor.modules.generate.service.IGenerateDevDetailService;
import org.hentor.modules.generate.service.IGenerateDevMainService;
import org.hentor.modules.generate.vo.GenerateCodeVO;
import org.hentor.generate.entity.GenerateDevDetail;
import org.hentor.generate.entity.GenerateDevMain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * <p>
 * 代码生成 前端控制器
 * </p>
 *
 * @author wzd
 * @since 2023-06-12
 */
@Api(tags = "代码生成")
@RestController
@RequestMapping("/generate")
@Slf4j
public class GenerateDevController {

    @Autowired
    private IGenerateDevMainService generateDevMasterService;
    @Autowired
    private IGenerateDevDetailService generateDevDetailService;

    @Autowired
    private RedisUtil redisUtil;


    @ApiOperation("保存配置信息")
    @RequestMapping(value = "/saveGenerateTable", method = RequestMethod.POST)
    public Result<String> saveGenerateTable(@RequestBody GenerateDevSaveDTO generateDevSaveDTO){
        try {
            generateDevMasterService.saveGenerateTable(generateDevSaveDTO);
        }catch (Exception e){
            log.error("代码生成配置信息保存失败",e);
            return Result.error("保存失败"+e.getMessage());
        }
        return Result.ok("保存成功");
    }


    @ApiOperation("查询配置信息")
    @RequestMapping(value = "/selectGenerateTable", method = RequestMethod.POST)
    public Result<IPage<GenerateDevMain>> selectGenerateTable(@RequestBody GenerateDevSelectDTO generateDevSelectDTO){
        try {
            return Result.OK(generateDevMasterService.selectGenerateTable(generateDevSelectDTO));
        }catch (Exception e){
            log.error("代码生成配置信息查询失败",e);
            return Result.error("查询失败"+e.getMessage());
        }
    }

    @ApiOperation("修改配置信息")
    @RequestMapping(value = "/updateGenerateTable", method = RequestMethod.POST)
    public Result<String> updateGenerateTable(@RequestBody GenerateDevSaveDTO generateDevSaveDTO){
        try {
            generateDevMasterService.updateGenerateTable(generateDevSaveDTO);
        }catch (Exception e){
            log.error("代码生成配置信息修改失败",e);
            return Result.error("修改失败"+e.getMessage());
        }
        return Result.ok("修改成功");
    }

    @ApiOperation("删除配置信息")
    @RequestMapping(value = "/deleteGenerateTable", method = RequestMethod.POST)
    public Result<String> deleteGenerateTable(@RequestBody GenerateDevMain generateDevMain){
        try {
            generateDevMasterService.deleteGenerateTable(generateDevMain);
        }catch (Exception e){
            log.error("代码生成表单信息删除失败",e);
            return Result.error("删除失败"+e.getMessage());
        }
        return Result.ok("删除成功");
    }


    @ApiOperation("查询配置明细")
    @RequestMapping(value = "/selectGenerateTableDetail", method = RequestMethod.POST)
    public Result<List<GenerateDevDetail>> selectGenerateTableDetail(@RequestBody GenerateDevMain generateDevMain){
        try {
            LambdaQueryWrapper<GenerateDevDetail> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(GenerateDevDetail::getMainId, generateDevMain.getId());
            queryWrapper.orderByAsc(GenerateDevDetail::getSortOrder);
            return Result.OK(generateDevDetailService.list(queryWrapper));
        }catch (Exception e){
            log.error("代码生成配置信息查询失败",e);
            return Result.error("查询失败"+e.getMessage());
        }
    }


    @ApiOperation("同步数据库")
    @RequestMapping(value = "/syncDataBase", method = RequestMethod.POST)
    public Result<String> syncDataBase(@RequestBody GenerateDevMain generateDevMain){
        try {
            generateDevMasterService.syncDataBase(generateDevMain);
        }catch (Exception e){
            log.error("代码生成同步数据库失败",e);
            return Result.error("同步数据库失败"+e.getMessage());
        }
        return Result.ok("同步数据库成功");
    }

    @ApiOperation("生成代码文件")
    @RequestMapping(value = "/codeFileGenerator", method = RequestMethod.POST)
    public Result<GenerateCodeVO> codeFileGenerator(@RequestBody GenerateCodeDTO generateCodeDTO) {
        try {
            GenerateCodeVO generateCodeVO = generateDevMasterService.codeFileGenerator(generateCodeDTO);
            return Result.ok(generateCodeVO);
        }catch (Exception e){
            log.error("代码生成代码生成失败",e);
            return Result.error("代码生成代码生成失败"+e.getMessage());
        }
    }


    @ApiOperation(value = "下载代码文件", httpMethod = "POST",produces = 	MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @RequestMapping(value = "/downloadCodeZip", method = RequestMethod.POST)
    public ResponseEntity<InputStreamResource> downloadCodeZip(@RequestBody GenerateCodeVO generateCodeDTO, HttpServletResponse response) throws Exception {

        List<String> filePaths = (List<String>) redisUtil.get("CODE_FILE_KEY_" + generateCodeDTO.getFileKey());
        List<Path> files = new ArrayList<>();
        filePaths.forEach(x->{
            files.add(Paths.get(x));
        });
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ZipArchiveOutputStream zipOutputStream = new ZipArchiveOutputStream(byteArrayOutputStream)) {
            createZipWithFiles(files, zipOutputStream);
        }

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        InputStreamResource resource = new InputStreamResource(byteArrayInputStream);

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=download.zip");

        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(byteArrayInputStream.available())
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }

    private void createZipWithFiles(List<Path> files, ZipArchiveOutputStream zipOutputStream) throws Exception {
        for (Path file : files) {
            addFileToZip(file, zipOutputStream);
        }
        zipOutputStream.finish();
    }

    private void addFileToZip(Path file, ZipArchiveOutputStream zipOutputStream) throws Exception {
        try (InputStream inputStream = new FileInputStream(file.toFile())) {
            String regx = "";
            String targetSubstring = "/temp";
            int indexOfTargetSubstring = file.toString().indexOf(targetSubstring);
            if (indexOfTargetSubstring != -1) {
                regx = file.toString().substring(0, indexOfTargetSubstring);
            }
            String filePath = file.toString().replaceFirst(regx, "");
            ZipArchiveEntry entry = new ZipArchiveEntry(filePath);
            zipOutputStream.putArchiveEntry(entry);
            IOUtils.copy(inputStream, zipOutputStream);
            zipOutputStream.closeArchiveEntry();
        }
    }

}
